/**
 * Yolema.com Inc.
 * Copyright (c) 2011-2012 All Rights Reserved.
 */
package org.grayrabbit.pay.facade.impl;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.grayrabbit.pay.chinapnr.common.ChinapnrGate;
import org.grayrabbit.pay.chinapnr.config.ChinapnrInfo;
import org.grayrabbit.pay.chinapnr.params.ChinapnrPayForm;
import org.grayrabbit.pay.chinapnr.params.ChinapnrRefundForm;
import org.grayrabbit.pay.chinapnr.result.ChinapnrPayResult;
import org.grayrabbit.pay.chinapnr.result.ChinapnrRefundResult;
import org.grayrabbit.pay.chinapnr.result.PayStatusEnum;
import org.grayrabbit.pay.core.biz.manager.trade.ChinaPnrTradeManager;
import org.grayrabbit.pay.core.biz.vo.ChinapnrTradeVo;
import org.grayrabbit.pay.core.model.common.SystemConstants;
import org.grayrabbit.pay.core.model.entity.system.AppSystem;
import org.grayrabbit.pay.core.model.entity.transaction.Trade;
import org.grayrabbit.pay.core.model.enums.TradeEnum;
import org.grayrabbit.pay.core.model.exception.PayException;
import org.grayrabbit.pay.core.model.results.GenericsResult;
import org.grayrabbit.pay.core.model.results.PayResult;
import org.grayrabbit.pay.core.model.utils.AppSystemUtil;
import org.grayrabbit.pay.facade.ChinapnrClient;
import org.grayrabbit.pay.facade.bean.ChinapnrTrade;
import org.grayrabbit.pay.facade.common.Result;
import org.grayrabbit.pay.facade.common.TradeConstants;
import org.grayrabbit.pay.facade.common.TradeConstants.RefundCode;
import org.grayrabbit.pay.facade.common.TradeConstants.TradeCode;
import org.grayrabbit.pay.facade.convert.ChinapnrTradeConvert;
import org.grayrabbit.pay.facade.enums.ErrorCodeEnum;
import org.grayrabbit.pay.facade.exception.ParamException;
import org.grayrabbit.pay.facade.exception.PayClientException;
import org.grayrabbit.pay.facade.exception.PermissionException;
import org.grayrabbit.pay.http.ChinapnrPay;
import org.grayrabbit.pay.utils.GenerateTradeIdUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * 
 * @author wy
 * @version $Id: ChinapnrClientImpl.java, v 0.1 2012-11-6 下午4:03:34 wy Exp $
 */
@Component
public class ChinapnrClientImpl implements ChinapnrClient {

    private static final Logger  LOG = LoggerFactory.getLogger(ChinapnrClientImpl.class);

    @Value("${pay.url}")
    private String               payUrl;

    @Autowired
    private ChinaPnrTradeManager chinaPnrTradeManager;

    /**
     * 获取支付结果状态,汇付
     * 
     * @param chinapnrTrade
     * @return
     */
    public Result<TradeCode> payResult(ChinapnrTrade chinapnrTrade) {

        Result<TradeCode> result = new Result<TradeConstants.TradeCode>();
        try {
            validate(chinapnrTrade);
            Trade queryTrade = new Trade();
            queryTrade.setAppSystemCode(chinapnrTrade.getSystemCode());
            queryTrade.setOrderId(chinapnrTrade.getOrderId());
            queryTrade.setTradeAction(SystemConstants.ACTION_PAY);
            GenericsResult<Trade> genericsResult = chinaPnrTradeManager.queryPay(queryTrade);
            if (!genericsResult.isSuccess()) {
                result.setIsSuccess(false);
                result.setResultCode(genericsResult.getResultCode());
                result.setResultMsg(genericsResult.getResultMsg());
                return result;
            }

            Trade td = genericsResult.getResultData();

            if (!ChinapnrInfo.CHINAPNR_CODE.equals(td.getTradeTerminal())) {
                throw new PayClientException(ErrorCodeEnum.TRADE_TERMINAL);
            }

            result.setTradeId(td.getOrderTradeId());

            if (TradeEnum.PAY_FAIL.name().equals(td.getTradeStatus())) {
                result.setData(TradeCode.FAIL_PAY);
                result.setIsSuccess(true);
                return result;
            }
            // 此时还没返回结果，表示正在处理
            else if (TradeEnum.PAY_WAIT.name().equals(td.getTradeStatus())) {
                result.setData(TradeCode.PAY_IN_HAND);
                result.setIsSuccess(true);
                return result;
            } else {
                result.setData(TradeCode.PAY_SUCCESS);
                result.setIsSuccess(true);
                return result;
            }
        } catch (PayException pe) {
            result.setIsSuccess(Boolean.FALSE);
            result.setResultCode(pe.getResultCode());
            result.setResultMsg(pe.getResultMsg());
            LOG.error("PayException异常,{}:{}", pe.getResultCode(), pe.getResultMsg());
        } catch (PayClientException pce) {
            result.setIsSuccess(Boolean.FALSE);
            result.setResultCode(pce.getErrorCode());
            result.setResultMsg(pce.getErrorMsg());
            LOG.error("PayClientException异常,{}:{}", pce.getErrorCode(), pce.getErrorMsg());
        } catch (Exception e) {
            result.setIsSuccess(Boolean.FALSE);
            result.setResultCode(Result.DEFAULT_EXCEPTION);
            result.setResultMsg(e.getMessage());
            LOG.error("未知异常", e);
        }
        return result;
    }

    /**
     * 发起交易,汇付
     * 
     * @param chinapnrTrade
     * @return
     */
    public Result<TradeCode> getChinapnrPay(ChinapnrTrade chinapnrTrade) {

        Result<TradeCode> result = new Result<TradeConstants.TradeCode>();
        try {
            validate(chinapnrTrade);

            ChinapnrTradeVo chinapnrTradeVo = new ChinapnrTradeVo();
            ChinapnrTradeConvert.convert(chinapnrTrade, chinapnrTradeVo);

            // 保存，这个事务一定要提交，要不后面的处理查不到数据
            GenericsResult<Trade> genericsResult = chinaPnrTradeManager.saveTrade(chinapnrTradeVo);

            if (!genericsResult.isSuccess()) {
                result.setIsSuccess(false);
                result.setResultCode(genericsResult.getResultCode());
                result.setResultMsg(genericsResult.getResultMsg());
                return result;
            }

            Trade trade = genericsResult.getResultData();

            // 转换订单信息，调用汇付相关接口
            ChinapnrPayForm form = new ChinapnrPayForm();
            ChinapnrPay pay = new ChinapnrPay();
            form.setCardNo(chinapnrTrade.getCardNo());
            form.setCvv2(chinapnrTrade.getCvv2());
            form.setGate(ChinapnrGate.valueOfGate(chinapnrTrade.getGate()));
            form.setIdNo(chinapnrTrade.getIdNo());
            form.setIdType(chinapnrTrade.getIdType());
            form.setMerPriv("OrderId=" + chinapnrTrade.getOrderId());
            form.setMobNo(chinapnrTrade.getMobNo());
            form.setMoney(chinapnrTrade.getMoney().getChinapnrPay());
            form.setName(chinapnrTrade.getIdName());
            form.setValidDate(chinapnrTrade.getValidDate());
            form.setTradeId(trade.getOrderTradeId());
            // ------------提交form组装完毕

            // 发送交易请求
            LinkedHashMap<String, String> map = pay.pay(form, payUrl);

            ChinapnrPayResult chinapnrPayResult = ChinapnrPayResult.convertResponseMap(map);

            result.setTradeId(trade.getOrderTradeId());
            if (chinapnrPayResult.getStatus() == PayStatusEnum.SUCCESS) {

                Trade queryTrade = new Trade();
                queryTrade.setOrderTradeId(trade.getOrderTradeId());
                queryTrade.setTradeAction(SystemConstants.ACTION_PAY);
                // 需要到数据库里验证
                GenericsResult<Trade> t = chinaPnrTradeManager.queryPay(queryTrade);
                // 没处理成功的设置为处理成功，这个时候一般是系统出现了问题
                if (!t.isSuccess()) {
                    // 去数据库里查询，查看是否已经变更
                    result.setData(TradeCode.PAY_IN_HAND);
                    result.setIsSuccess(true);
                    return result;
                }

                Trade td = t.getResultData();
                if (TradeEnum.PAY_FAIL.name().equals(td.getTradeStatus())) {
                    result.setData(TradeCode.FAIL_PAY);
                    result.setIsSuccess(true);
                    return result;
                }
                // 此时还没返回结果，表示正在处理
                else if (!TradeEnum.PAY_SUCCESS.name().equals(td.getTradeStatus())) {
                    result.setData(TradeCode.PAY_IN_HAND);
                    result.setIsSuccess(true);
                    return result;
                } else {
                    result.setData(TradeCode.PAY_SUCCESS);
                    result.setIsSuccess(true);
                    return result;
                }

            } else if (chinapnrPayResult.getStatus() == PayStatusEnum.IN_HAND) {
                // 去数据库里查询，查看是否已经变更
                result.setData(TradeCode.PAY_IN_HAND);
                result.setIsSuccess(true);
            } else {

                result.setIsSuccess(false);
                result.setResultCode(chinapnrPayResult.getResponseCode());
                result.setResultMsg(chinapnrPayResult.getCause());
            }
            return result;

        } catch (PayException pe) {
            result.setIsSuccess(Boolean.FALSE);
            result.setResultCode(pe.getResultCode());
            result.setResultMsg(pe.getResultMsg());
            LOG.error("PayException异常,{}:{}", pe.getResultCode(), pe.getResultMsg());
        } catch (PayClientException pce) {
            result.setIsSuccess(Boolean.FALSE);
            result.setResultCode(pce.getErrorCode());
            result.setResultMsg(pce.getErrorMsg());
            LOG.error("PayClientException异常,{}:{}", pce.getErrorCode(), pce.getErrorMsg());
        } catch (Exception e) {
            result.setIsSuccess(Boolean.FALSE);
            result.setResultCode(Result.DEFAULT_EXCEPTION);
            result.setResultMsg(e.getMessage());
            LOG.error("未知异常", e);
        }
        return result;

    }

    /**
     * 获取交易网关,汇付
     * 
     * @return
     */
    public Result<Map<String, String>> chinapnrGate() {
        ChinapnrGate[] gates = ChinapnrGate.values();
        Map<String, String> map = new HashMap<String, String>();
        for (ChinapnrGate gate : gates) {
            map.put(gate.getGate(), gate.getBank());
        }

        Result<Map<String, String>> result = new Result<Map<String, String>>();
        result.setData(map);
        return result;
    }

    /**
     * 发起退款
     * 
     * @param chinapnrTrade
     * @return
     */
    public Result<RefundCode> refundTrade(ChinapnrTrade chinapnrTrade) {

        Result<RefundCode> result = new Result<TradeConstants.RefundCode>();
        try {
            validate(chinapnrTrade);

            ChinapnrTradeVo ChinapnrTradeVo = new ChinapnrTradeVo();

            ChinapnrTradeConvert.convert(chinapnrTrade, ChinapnrTradeVo);

            GenericsResult<Trade> genericsResult = chinaPnrTradeManager
                .saveRefundTrade(ChinapnrTradeVo);

            if (!genericsResult.isSuccess()) {
                result.setIsSuccess(false);
                result.setResultCode(genericsResult.getResultCode());
                result.setResultMsg(genericsResult.getResultMsg());
                return result;
            }
            // 获取退款交易数据
            Trade trade = genericsResult.getResultData();
            String oldTradeId = trade.getOrderTradeId();
            String tradeId = GenerateTradeIdUtil.generateChinapnrRefundId(trade.getTradeId());
            ChinapnrPay pay = new ChinapnrPay();
            ChinapnrRefundForm form = new ChinapnrRefundForm();
            form.setMoney(chinapnrTrade.getMoney().getChinapnrPay());
            form.setTradeId(tradeId);
            form.setOldTradeId(oldTradeId);
            LinkedHashMap<String, String> map = pay.refund(form);

            ChinapnrRefundResult refundResult = ChinapnrRefundResult.convertResponseMap(map);

            boolean success = refundResult.getSuccess();

            PayResult payResult = chinaPnrTradeManager.updateRefundTrade(trade, success);

            result.setTradeId(tradeId);

            // 已经退款成功了，但是保存数据失败
            if (payResult.isSuccess()) {
                LOG.error("更新汇付的退款交易结果出错");
            }

            if (success) {
                result.setIsSuccess(true);
                result.setData(RefundCode.REFUND_SUCCESS);
            } else {
                result.setIsSuccess(false);
                result.setData(RefundCode.REFUND_FAIL);
                result.setResultCode(refundResult.getResponseCode());
                result.setResultMsg(refundResult.getCause());
            }

            return result;

        } catch (PayException pe) {
            result.setIsSuccess(Boolean.FALSE);
            result.setResultCode(pe.getResultCode());
            result.setResultMsg(pe.getResultMsg());
            LOG.error("PayException异常,{}:{}", pe.getResultCode(), pe.getResultMsg());
        } catch (PayClientException pce) {
            result.setIsSuccess(Boolean.FALSE);
            result.setResultCode(pce.getErrorCode());
            result.setResultMsg(pce.getErrorMsg());
            LOG.error("PayClientException异常,{}:{}", pce.getErrorCode(), pce.getErrorMsg());
        } catch (Exception e) {
            result.setIsSuccess(Boolean.FALSE);
            result.setResultCode(Result.DEFAULT_EXCEPTION);
            result.setResultMsg(e.getMessage());
            LOG.error("未知异常", e);
        }
        return result;
    }

    /**
     * 验证数据
     * 
     * @param chinapnrTrade
     */
    private void validate(ChinapnrTrade chinapnrTrade) {
        if (chinapnrTrade == null) {
            throw new ParamException("chinaPayTrade is Null");
        }
        if (StringUtils.isBlank(chinapnrTrade.getSystemCode())) {
            throw new PermissionException();
        }
        if (StringUtils.isBlank(chinapnrTrade.getOrderId())) {
            throw new ParamException("OrderId is Null");
        }
        if (chinapnrTrade.getOrderId().length() > 32) {
            throw new ParamException("OrderId length max 32");
        }

        AppSystem appSystem = AppSystemUtil.getAppSystem(chinapnrTrade.getSystemCode());
        if (appSystem == null) {
            throw new PermissionException();
        }
    }

}
